package TL3 (sysTL, TL) where {

  -- Simple model of a traffic light

  -- Version 3: Add pedestrian request button.  When pressed,
  --     wait for next transition out of Red
  --     goto GreenPed      for pedGreenDelay  secs
  --     goto AmberPed      for pedAmberDelay  secs
  --     goto AllRed        for allRedDelay    secs
  --     goto next green    (i.e., don't break the cycle)

  interface TL = { ped_button_push :: Action;
                 };

  data TLstates = AllRed
                | GreenNS  | AmberNS
                | GreenE   | AmberE
                | GreenW   | AmberW
                | GreenPed | AmberPed
                deriving (Eq, Bits);

  type Time32 = UInt 5;

  sysTL :: Module TL;
  sysTL =
      module {
          state :: Reg TLstates;
          state <- mkReg AllRed;

          next_green :: Reg TLstates;
          next_green <- mkReg GreenNS;

          secs :: Reg Time32;
          secs <- mkReg 0;

          ped_button_pushed :: Reg Bool;
          ped_button_pushed <- mkReg False;

          let {
              allRedDelay :: Time32;
              allRedDelay = 2;

              amberDelay :: Time32;
              amberDelay = 4;

              ns_green_delay :: Time32;
              ns_green_delay = 20;

              ew_green_delay :: Time32;
              ew_green_delay = 10;

              pedGreenDelay :: Time32;
              pedGreenDelay = 10;

              pedAmberDelay :: Time32;
              pedAmberDelay = 6;
          };
      interface {
          ped_button_push   = ped_button_pushed  := True;
      };
      addRules $
        rules {
          "wait": when True ==> secs := secs + 1
        }

        +>

        rules {
          "fromAllRed":
            when (state == AllRed) && ((secs + 1) >= allRedDelay)
              ==> action { state           := if not ped_button_pushed then  next_green
                                       else                           GreenPed;
                    secs            := 0;
                    ped_button_pushed := False };

          "fromGreenPed":
            when (state == GreenPed) && ((secs + 1) >= pedGreenDelay)
              ==> action { state := AmberPed; secs := 0; };

          "fromAmberPed":
            when (state == AmberPed) && ((secs + 1) >= pedAmberDelay)
              ==> action { state := AllRed; secs := 0; };

          "fromGreenNS":
            when (state == GreenNS) && ((secs + 1) >= ns_green_delay)
              ==> action { state := AmberNS; secs := 0; };

          "fromAmberNS":
            when (state == AmberNS) && ((secs + 1) >= amberDelay)
              ==> action { state := AllRed; secs := 0; next_green := GreenE; };

          "fromGreenE":
            when (state == GreenE) && ((secs + 1) >= ew_green_delay)
              ==> action { state := AmberE; secs := 0; };

          "fromAmberE":
            when (state == AmberE) && ((secs + 1) >= amberDelay)
              ==> action { state := AllRed; secs := 0; next_green := GreenW; };

          "fromGreenW":
            when (state == GreenW) && ((secs + 1) >= ew_green_delay)
              ==> action { state := AmberW; secs := 0; };

          "fromAmberW":
            when (state == AmberW) && ((secs + 1) >= amberDelay)
              ==> action { state := AllRed; secs := 0; next_green := GreenNS; };
        }
      }
}
